

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SpringLayout;
import javax.swing.table.AbstractTableModel;

public class Main {

	private JFrame frmMachineStateProblem;
	private JTextField nStates;
	private JTextField nInputs;
	private JPanel panel;
	private JScrollPane scrollPane;
	private JTable table;
	private String[][] data;
	private String[] header;
	public static int states;
	public static int inputs;
	private SpringLayout springLayout;


	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Main window = new Main();
					window.frmMachineStateProblem.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public Main() {
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frmMachineStateProblem = new JFrame();
		frmMachineStateProblem.setTitle("Machine State Problem Solver");
		frmMachineStateProblem.setBounds(100, 100, 640, 120);
		frmMachineStateProblem.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		springLayout = new SpringLayout();
		frmMachineStateProblem.getContentPane().setLayout(springLayout);

		panel = new JPanel();
		springLayout.putConstraint(SpringLayout.NORTH, panel, 11, SpringLayout.NORTH, frmMachineStateProblem.getContentPane());
		springLayout.putConstraint(SpringLayout.WEST, panel, 10, SpringLayout.WEST, frmMachineStateProblem.getContentPane());
		springLayout.putConstraint(SpringLayout.SOUTH, panel, 58, SpringLayout.NORTH, frmMachineStateProblem.getContentPane());
		springLayout.putConstraint(SpringLayout.EAST, panel, 614, SpringLayout.WEST, frmMachineStateProblem.getContentPane());
		frmMachineStateProblem.getContentPane().add(panel);
		panel.setLayout(null);

		JLabel lblNumberOfStates = new JLabel("Number of States");
		lblNumberOfStates.setBounds(8, 3, 125, 14);
		panel.add(lblNumberOfStates);

		JLabel lblNumberOfInputs = new JLabel("Number of inputs");
		lblNumberOfInputs.setBounds(8, 28, 125, 14);
		panel.add(lblNumberOfInputs);

		nStates = new JTextField();
		nStates.setBounds(143, 3, 86, 20);
		panel.add(nStates);
		nStates.setColumns(10);

		nInputs = new JTextField();
		nInputs.setBounds(143, 26, 86, 20);
		panel.add(nInputs);
		nInputs.setColumns(10);

		final JButton btnStartCreation = new JButton("Start Creation");
		btnStartCreation.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if(btnStartCreation.getText().equals("Done")){
					startSolving();
					return;
				}
				try{
					states = Integer.parseInt(nStates.getText());
					inputs = Integer.parseInt(nInputs.getText());

					displayInputTable();
					btnStartCreation.setText("Done");

					nStates.setEnabled(false);
					nInputs.setEnabled(false);
				}catch (Exception e){
					JOptionPane.showMessageDialog(frmMachineStateProblem,
						    "Please enter positive integers.",
						    "Integer Format Error",
						    JOptionPane.ERROR_MESSAGE);
				}
			}
		});
		btnStartCreation.setBounds(291, 3, 138, 23);
		panel.add(btnStartCreation);
		
				JButton btnBrowse = new JButton("Browse");
				btnBrowse.setBounds(439, 3, 155, 23);
				panel.add(btnBrowse);
				btnBrowse.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent arg0) {
						final JFileChooser fc = new JFileChooser();
						int returnVal = fc.showSaveDialog(frmMachineStateProblem);
						if (returnVal == JFileChooser.APPROVE_OPTION) {
							try {
								Object[] tmp = FileInputReader.getTableFrom(fc.getSelectedFile());
								states = (Integer) tmp[1];
								inputs = (Integer) tmp[2];
								
								SolverGUI solver = new SolverGUI((TransitionTable) tmp[0]);
								solver.setVisible();
							} catch (FileNotFoundException e) {
								JOptionPane.showMessageDialog(frmMachineStateProblem,
									    "Couldn't open file.",
									    "File Not Found Error",
									    JOptionPane.ERROR_MESSAGE);
							}catch (Exception e){
								JOptionPane.showMessageDialog(frmMachineStateProblem,
									    "Couldn't load transition table from file.\nFile mal-formated",
									    "Transition Table Error",
									    JOptionPane.ERROR_MESSAGE);
							}
						}
					}
				});
	}

	protected void startSolving() {
		try{
		Pattern pattern = Pattern.compile("([A-Z])/([0-1])");
		Matcher matcher;
		TransitionTable t = new TransitionTable(inputs);
		State[] row;
		for(int i=0; i<states; i++){
			row = new State[inputs+1];
			row[0] = new State(data[i][0]);
			for(int j=1; j<=inputs; j++){
				matcher = pattern.matcher(data[i][j]);
				if(matcher.find()){
					row[j] = new State(matcher.group(1),Integer.parseInt(matcher.group(2)));
				}
			}
			t.addRow(row);
		}
		SolverGUI solver = new SolverGUI(t);
		solver.setVisible();
		}catch (Exception e){
			JOptionPane.showMessageDialog(frmMachineStateProblem,
				    "An error occured while loading the transition table.",
				    "Transition Table Error",
				    JOptionPane.ERROR_MESSAGE);
		}
	}

	protected void displayInputTable() {
		data = new String[states][inputs+1];

		for(int i=0; i<data.length; i++){
			data[i][0] = (char)('A'+i)+"";
			for(int j=1; j<data[i].length; j++)
				data[i][j] = "A/0";
		}

		header = new String[inputs+1];
		header[0]= "";
		for(int i=1; i<header.length; i++)
			header[i]= "Input"+i;

		frmMachineStateProblem.setBounds(100, 100, 640, 520);
		
		table = new JTable(new MyTableModel());
		scrollPane =  new JScrollPane(table);
		springLayout.putConstraint(SpringLayout.NORTH, scrollPane, 10, SpringLayout.SOUTH, panel);
		springLayout.putConstraint(SpringLayout.WEST, scrollPane, 10, SpringLayout.WEST, frmMachineStateProblem.getContentPane());
		springLayout.putConstraint(SpringLayout.SOUTH, scrollPane, -10, SpringLayout.SOUTH, frmMachineStateProblem.getContentPane());
		springLayout.putConstraint(SpringLayout.EAST, scrollPane, -10, SpringLayout.EAST, frmMachineStateProblem.getContentPane());
		frmMachineStateProblem.getContentPane().add(scrollPane);

		table.setFillsViewportHeight(true);
		
		scrollPane.setVisible(true);

		//		for(int i=1; i<header.length; i++)
		//			setUpInputColumn(table, table.getColumn(header[i]));
	}

	class MyTableModel extends AbstractTableModel {
		/**
		 * 
		 */
		private static final long serialVersionUID = 8378177373961738101L;

		public int getColumnCount() {
			return inputs+1;
		}

		public int getRowCount() {
			return data.length;
		}

		public String getColumnName(int col) {
			return header[col];
		}

		public Object getValueAt(int row, int col) {
			return data[row][col];
		}

		/*
		 * JTable uses this method to determine the default renderer/
		 * editor for each cell.  If we didn't implement this method,
		 * then the last column would contain text ("true"/"false"),
		 * rather than a check box.
		 */
		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Class getColumnClass(int c) {
			return "".getClass();
		}

		/*
		 * Don't need to implement this method unless your table's
		 * editable.
		 */
		public boolean isCellEditable(int row, int col) {
			//Note that the data/cell address is constant,
			//no matter where the cell appears onscreen.
			if (col ==0) {
				return false;
			} else {
				return true;
			}
		}

		public void setValueAt(Object value, int row, int col) {
			if(!((String)value).matches("[A-"+(char)('A'+states-1)+"]\\D[0-1]"))
				return;

			data[row][col] = ((String)value).toUpperCase();
			fireTableCellUpdated(row, col);
		}
	}
}
